// Copyright 2008-2009 BUGCOLLECT.COM
// Author: Remus Rusanu
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//                 http://bugcollect.com/terms
//

package com.bugcollect;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;

/**
 * This class handles submitting reports to bugcollect.com.<br/>
 * ReportingManager instances must be created with the proper
 * applciation credentials used when submitting reports.
 * It is recommended to keep one sngle the ReportingManager
 * instance for for the entire duration of the application lifetime
 * because of HTTP Digest authentication prototcol caching.
 * <br/>
 * The easiest way to submit a report is to call
 * <code>{@link ReportingManager#Submit(java.lang.String, java.lang.Throwable)}</code> when
 * an exception is caught:
 * <pre>
 * {@code
    ReportingManager reportingManager = new ReportingManager(
                applicationName, applicationPassword);
    try
    {
        ...
    }
    catch(Exception ex)
    {
        reportingManager.Submit("Exception when trying to ...", ex);
    }
 * }
 * </pre>
 * applicationName and applicationPassword must be the name and password
 * of an application credential added to your account with bugcollect.com.
 */
public class ReportingManager {
    private final static String submitionURL =
            "http://bugcollect.com/rest/reports";

    ReportAuthenticator authenticator;
    XMLOutputFactory xmlOutputFactory;

    private String _mainPackage;
    /**
     * The application main package name.
     * Used as the application name when submitting a report to bugcollect.com.
     */
    public String getMainPackage()
    {
        return _mainPackage;
    }
    /**
     * Sets the application name to be used for reporting incidents.
     */
    public void setMainPackage(
            String value)
    {
        _mainPackage = value;
    }

    private void initCommon()
    {
        xmlOutputFactory = XMLOutputFactory.newInstance();
        try
        {
            _mainPackage = System.getProperty("java.main.class");

            if (null == _mainPackage)
            {
                Map<String, String> environment = System.getenv();
                for(String variable : environment.keySet())
                {
                    if (variable.startsWith("JAVA_MAIN_CLASS", 0))
                    {
                        _mainPackage = environment.get(variable);
                        break;
                    }
                }
            }
        }
        catch(Exception ex)
        {
            // silent
        }

    }

    /**
     * Creates an instance of the <code>ReportingManager</code> that
     * uses the given credentials to submit reports
     * @param applicationName       the credetial name to be used
     * @param applicationPassword   the credential password to be used
     */
    public ReportingManager(
            String applicationName,
            String applicationPassword)
    {
        authenticator = new ReportAuthenticator (
                applicationName,
                applicationPassword);
        initCommon();
    }

    /**
     * Submits a report to bugcollect.com.
     * If supplied, the <code>{@link IResponseCallback}</code> <code>callbackDelegate</code> is invoked
     * on submittion completion.
     * @see IResponseCallback
     * @param report    the incident report
     */
    public void Submit(
            Report report,
            IResponseCallback callbackDelegate,
            Object state)
    {
        URL url = null;
        HttpURLConnection request = null;
        try
        {
            url = new URL(submitionURL);
            request = (HttpURLConnection) url.openConnection();

            report.setApplication(_mainPackage);

            request.setDoInput(true);
            request.setDoOutput(true);
            request.setRequestProperty("Content-Type", "text/xml");

            Authenticator.setDefault(authenticator);
            
            OutputStream sout = request.getOutputStream();
            XMLStreamWriter writer = xmlOutputFactory.createXMLStreamWriter(sout);
            writer.writeStartDocument();
            report.writeToXmlStream(writer);
            writer.writeEndDocument();
            writer.close();

            InputStream is = request.getInputStream();            
            Result result = new Result();
            result.parseXml(is);

            if (null != callbackDelegate)
            {
                callbackDelegate.responseReceived(report, result, state);
            }
            
        } catch(Throwable e)
        {
            if (null != callbackDelegate)
            {
                callbackDelegate.submittionException(report, e, request.getErrorStream(), state);
            }
        }

    }

    /**
     * Creates a new report and submits it to bugcollect.com
     * @param notes     any incident specific notes
     * @param e         the exception or error from which
     *                  the report is constructed
     * @see Report#Report(java.lang.String, java.lang.Throwable) 
     */
     public void Submit(
            String notes,
            Throwable e)
     {
         Report report = new Report(notes, e);
         Submit(report, null, null);
     }
}
